package megadrum.mididata;

public class Converter {

    public static byte[] concatenateByteArrays(byte[] b1, byte b2[]) {
        byte[] result = new byte[b1.length + b2.length];
        int j=0;
        for(int i=0; i<b1.length; i++){
            result[j] = b1[i];
            j++;
        }
        for(int i=0; i<b2.length; i++){
            result[j] = b2[i];
            j++;
        }
        return result;
    }
    public static byte[] subByteArray(byte[] b, int beginIndex, int endIndex) {
        byte[] b2 = new byte[endIndex - beginIndex];
        for (int i = beginIndex; i < endIndex; i++) {
            b2[i - beginIndex] = b[i];
        }
        return b2;
    }

    public static boolean[] subBooleanArray(boolean[] b, int beginIndex, int endIndex) {
        boolean[] b2 = new boolean[endIndex - beginIndex];
        for (int i = beginIndex; i < endIndex; i++) {
            b2[i - beginIndex] = b[i];
        }
        return b2;
    }

    public static byte[] insertBytes(byte[] b1, byte b2[], int pos) {
        if ((pos + b2.length) <= b1.length) {
            for (int i = 0; i < b2.length; i++) {
                b1[i + pos] = b2[i];
            }
        }
        return b1;
    }

    public static int getIntFromBitsInByte(byte b, int from, int to) { //order is from bit 7 to bit 0
        return (int) ((byte) (b >> to) & ones(from - to));
    }

    public static int getIntFromBitsInByte(byte b, int n) { //order is from bit 7 to bit 0
        return getIntFromBitsInByte(b, n, 0);
    }

    private static byte ones(int n) {
        boolean b[] = new boolean[8];
        for (int i = 7; i > 7 - n; i--) {
            b[i] = true;
        }
        return bitsToByte(b);
    }

    public static int getIntFromBitInByte(byte b, int selectedBit) { //order is from bit 7 to bit 0
        return (int) ((byte) (b >> selectedBit) & (byte) 0x01);
    }

    public static boolean getBooleanFromBitInByte(byte b, int selectedBit) { //order is from bit 7 to bit 0
        return (byte) ((b >> selectedBit) & 0x01) == (byte) 0x01;
    }

    public static byte[] intToTwoBytes(int i) { //byte[0] is MSB and byte[1] is LSB
        byte b = (byte) i;
        byte b2[] = new byte[2];
        b2[0] = (byte) (((byte) (b >> 4)) & 0x0F);
        b2[1] = (byte) (b & 0x0F);
        return b2;
    }

    public static int twoBytesToOneInt(byte b1, byte b2) { //b1 is MSB and b2 is LSB
        b1 = (byte) (((byte) (b1 << 4)) & 0xF0);
        b2 = (byte) (b2 & 0x0F);
        return 0x000000FF & (b1 | b2);
    }

    public static byte[] intToFourBytes(int i) { //byte[0] is MSB and byte[3] is LSB
        byte b0 = (byte) (i & 0xFF);
        byte b1 = (byte) ((i >> 8) & 0xFF);
        byte b2[] = new byte[4];
        b2[0] = (byte) (((byte) (b0 >> 4)) & 0x0F);
        b2[1] = (byte) (b0 & 0x0F);
        b2[2] = (byte) (((byte) (b1 >> 4)) & 0x0F);
        b2[3] = (byte) (b1 & 0x0F);
        return b2;
    }

    public static int fourBytesToOneInt(byte b1, byte b2, byte b3, byte b4) { //b1 is MSB and b4 is LSB
        b1 = (byte) (((byte) (b1 << 4)) & 0xF0);
        b2 = (byte) (b2 & 0x0F);
        b3 = (byte) (((byte) (b3 << 4)) & 0xF0);
        b4 = (byte) (b4 & 0x0F);
        short s1 = (short) ((((b3 | b4) & 0xFF) << 8));
        short s2 = (short) ((b1 | b2) & 0xFF);
        short s3 = (short) (s1 | s2);
        return (int) s3;
    }

    public static int eightBytesToOneInt(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8) { //b1 is MSB and b4 is LSB
        b1 = (byte) (((byte) (b1 << 4)) & 0xF0);
        b2 = (byte) (b2 & 0x0F);
        b3 = (byte) (((byte) (b3 << 4)) & 0xF0);
        b4 = (byte) (b4 & 0x0F);
        b5 = (byte) (((byte) (b5 << 4)) & 0xF0);
        b6 = (byte) (b6 & 0x0F);
        b7 = (byte) (((byte) (b7 << 4)) & 0xF0);
        b8 = (byte) (b8 & 0x0F);
        int s1 = ((b7 | b8) & 0xFF) << 24;
        int s2 = ((b5 | b6) & 0xFF) << 16;
        int s3 = ((b3 | b4) & 0xFF) << 8;
        int s4 = (b1 | b2) & 0xFF;
        int s5 = (int) (s1 | s2 | s3 | s4);
        return s5;
    }

    public static int bitsToInt(boolean b[]) { //b[0] is bit 7 and b[7] is bit 0
        String s = "";
        for (int i = 0; i < b.length; i++) {
            if (b[i]) {
                s += "1";
            } else {
                s += "0";
            }
        }
        return Integer.parseInt(s, 2);
    }

    public static byte bitsToByte(boolean b[]) { //b[0] is bit 7 and b[7] is bit 0
        return (byte) (bitsToInt(b) & 0xFF);
    }

    public static byte bitsToByte(boolean b7, boolean b6, boolean b5, boolean b4, boolean b3, boolean b2, boolean b1, boolean b0) {
        boolean b[] = new boolean[8];
        b[0] = b7;
        b[1] = b6;
        b[2] = b5;
        b[3] = b4;
        b[4] = b3;
        b[5] = b2;
        b[6] = b1;
        b[7] = b0;
        return (byte) (bitsToInt(b) & 0xFF);
    }

    public static byte bitsToNibble(boolean b[]) { //b[0] is bit 3 and b[3] is bit 0
        return (byte) (bitsToInt(b) & 0x0F);
    }

    public static byte bitsToNibble(boolean b3, boolean b2, boolean b1, boolean b0) {
        boolean b[] = new boolean[8];
        b[4] = b3;
        b[5] = b2;
        b[6] = b1;
        b[7] = b0;
        return (byte) (bitsToInt(b) & 0x0F);
    }

    public static boolean[] intToBits(int number, int numberOfBits) { //b[0] is bit 7 and b[7] is bit 0 (if numberOfBits=8)
        boolean b[] = new boolean[numberOfBits];
        String s = Integer.toString(number, 2);
        //acommodate string to number of bits
        int j = 0;
        if (s.length() >= numberOfBits) {
            s = s.substring(s.length() - numberOfBits);
        } else {
            for (; j < numberOfBits - s.length(); j++) {
                b[j] = false;
            }
        }
        int i = j;
        for (; j < numberOfBits; j++) {
            if (s.charAt(j - i) == '1') {
                b[j] = true;
            } else {
                b[j] = false;
            }
        }
        return b;
    }

    public static boolean[] byteToBits(byte b) { //b[0] is bit 7 and b[7] is bit 0
        return intToBits((int) b, 8);
    }

    public static boolean[] nibbleToBits(byte b) { //b[0] is bit 3 and b[3] is bit 0
        return intToBits((int) b, 4);
    }

    public static String checksum(String formattedHexString) {
        byte[] checkB = new byte[(int) (formattedHexString.length() / 2)];
        for (int i = 0; i < checkB.length; i++) {
            checkB[i] = (byte) (Integer.parseInt(formattedHexString.substring(i * 2, i * 2 + 2), 16));
        }
        byte b = checksum(checkB);
        String s = Integer.toString(0x0000000F & (b >> 4), 16).toUpperCase() +
                Integer.toString(0x0000000F & b, 16).toUpperCase();
        return s;
    }

    public static byte checksum(byte[] line) {
        //add up bytes
        int sum = 0x00000000;
        for (int i = 0; i < line.length; i++) {
            sum = sum + (0x000000FF & line[i]);
        }
        //get LSB, 2-complement it, and mask with FF
        return (byte) ((((sum & 0x000000FF) ^ 0xFF) + 0x00000001) & 0xFF);
    }
}
